/*
 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
 *
 *  This file is licensed to You under the Common Public License (CPL);
 *  You may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/cpl1.0.php
 *
 *  See the COPYRIGHT.txt file distributed with this work for information
 *  regarding copyright ownership.
 */
package org.mmtk.plan.metrix;

import org.mmtk.plan.CollectorContext;
import org.mmtk.plan.StopTheWorldCollector;
import org.mmtk.plan.TraceLocal;
import org.mmtk.policy.MarkSweepLocal;
import org.mmtk.utility.Log;
import org.mmtk.vm.VM;
import org.vmmagic.pragma.Inline;
import org.vmmagic.pragma.Uninterruptible;

/**
 * This class implements <i>per-collector thread</i> behavior
 * and state for the <i>MS</i> plan, which implements a full-heap
 * mark-sweep collector.<p>
 *
 * Specifically, this class defines <i>MS</i> collection behavior
 * (through <code>trace</code> and the <code>collectionPhase</code>
 * method).<p>
 *
 * @see Metrix for an overview of the mark-sweep algorithm.<p>
 *
 * @see Metrix
 * @see MetrixMutator
 * @see StopTheWorldCollector
 * @see CollectorContext
 */
@Uninterruptible
public class MetrixCollector extends StopTheWorldCollector {

  /****************************************************************************
   * Instance fields
   */
  private MetrixTraceLocal traceDepth;
  private TraceBranch ballancer;
  protected MarkSweepLocal ms; 

  /****************************************************************************
   * Initialization
   */

  /**
   * Constructor
   */
  public MetrixCollector() {
    this.traceDepth = new MetrixTraceLocal(global().pilotTrace);
    this.ballancer = global().ballancer;
    ms = new MarkSweepLocal(Metrix.myMsSpace);
  }

  /****************************************************************************
	 * 
	 * Collection
	 */

	/**
	 * Perform a per-collector collection phase.
	 * 
	 * @param phaseId
	 *            The collection phase to perform
	 * @param primary
	 *            Perform any single-threaded activities using this thread.
	 */
	@Inline
	public void collectionPhase(short phaseId, boolean primary) {
		if (phaseId == Metrix.PREPARE) {
			super.collectionPhase(phaseId, primary);
			this.ms.prepare();
			this.traceDepth.prepare();
			this.traceDepth.setMetricClosure();
			return;
		}
		
		if (phaseId == Metrix.prepareClosure) {
			if(!Metrix.isFirstClosure()) {
				this.logMessage("prepareClosure - each collector saves collected roots and values for the next closure");
				this.traceDepth.zeroClosureCounts();
				this.traceDepth.restorePreClosureState();
			}	
			if(!Metrix.isLastClosure()) {
				this.logMessage("prepareClosure - each collector saves collected roots and values for the next closure");
				this.traceDepth.zeroClosureCounts();
				this.traceDepth.savePreClosureState(true);
			}
			if(!Metrix.isConcurrentClosure()) {
				this.logMessage("prepareClosure - each collector flushes its local roots and values into global structures");
				this.traceDepth.flush();
				//TODO combine counters
			}
			return;
		}

		//not active, will be removed
		if (phaseId == Metrix.concClosure1 || phaseId == Metrix.concClosure2) {		
			this.traceDepth.printClosureCounts("Before the addClosure1 closure ");
	    	//this.traceDepth.zeroClosureCounts();
	    	//this.traceDepth.savePreClosureState(true);
	    	this.traceDepth.completeTrace(true, true);
	    	//this.traceDepth.restorePreClosureState();
	    	this.traceDepth.printClosureCounts("After the addClosure1 closure ");
	    	logMessage("------------------------");
	    	this.traceDepth.setMetricClosure();			
			return;
		}
		
		if (phaseId == Metrix.depthClosure) {
			if(primary) {
			this.traceDepth.printClosureCounts("Before the addClosure2 closure ");		
			//this.traceDepth.zeroClosureCounts();
	    	//this.traceDepth.savePreClosureState(true);
	    	//Exchange two lines below to perform regular closure
	    	this.traceDepth.completeMetrixTrace(true, true);
	    	//this.traceDepth.completeTrace(true);
	    	//this.traceDepth.restorePreClosureState();
	    	this.traceDepth.printClosureCounts("After the addClosure2 closure ");
	    	logMessage("------------------------");
	    	this.traceDepth.setFixClosure();
			}
			return;
		}
		
		if (phaseId == Metrix.fixClosure) {
			if(primary) {
			this.traceDepth.printClosureCounts("Before the addClosure3 closure ");	
			//this.traceDepth.zeroClosureCounts();
	    	//this.traceDepth.savePreClosureState(true);
	    	//Exchange two lines below to perform regular closure
	    	this.traceDepth.completeFixTrace(ballancer, true);
	    	//this.traceDepth.completeTrace(true);
	    	//this.traceDepth.restorePreClosureState();
	    	this.traceDepth.printClosureCounts("After the addClosure3 closure ");
	    	logMessage("------------------------");
	    	this.traceDepth.setCheckClosure();
			}
			return;
		}
		
		if (phaseId == Metrix.depthCheckClosure) {
			if(primary) {
			this.traceDepth.printClosureCounts("Before the addClosure4 closure ");		
	    	//this.traceDepth.zeroClosureCounts();
	    	//this.traceDepth.savePreClosureState(false);
	    	//Exchange two lines below to perform regular closure
	    	this.traceDepth.completeMetrixTrace(false, false);
	    	//this.traceDepth.completeTrace(false);
	    	this.traceDepth.printClosureCounts("After the addClosure4 closure ");
	    	logMessage("------------------------");
	    	this.traceDepth.setRegularClosure();
			}
			return;
		}
		
	    if (phaseId == Metrix.CLOSURE) {
	    	this.traceDepth.printClosureCounts("Before the regular closure ");
	    	//this.traceDepth.zeroClosureCounts();
	    	this.traceDepth.completeTrace(false, true);
	    	this.traceDepth.printClosureCounts("After the regular closure ");
	    	logMessage("------------------------");
	        return;
	      }


		if (phaseId == Metrix.RELEASE) {
			super.collectionPhase(phaseId, primary);
			this.traceDepth.release();
			return;
		}

		super.collectionPhase(phaseId, primary);
	}

  /****************************************************************************
   *
   * Miscellaneous
   */

  /** @return The active global plan as an <code>MS</code> instance. */
  @Inline
  private static Metrix global() {
    return (Metrix) VM.activePlan.global();
  }

  /** @return The current trace instance. */
  public final TraceLocal getCurrentTrace() {
    return this.traceDepth;
  }
  
  	@Inline
	private void logMessage(String message) {
		if (Metrix.PILOT_VERBOSE) {
			Log.prependThreadId();
			Log.write(" - ");
			Log.writeln(message);
			Log.flush();
		}
	}
}
